#include "fileutil.h"
#include "commandutil.h"
#include "pathutil.h"

#include <QDir>
#include <QFile>
#include <QTextCodec>
#include <QCryptographicHash>

QString FileUtil::readContent(const QString &filePath, const QByteArray &codec)
{
    QFile file(filePath);
    if (!file.exists()) return "";
    file.open(QIODevice::ReadOnly);
    QTextCodec *tc = QTextCodec::codecForName(codec);
    QString content = tc->toUnicode(file.readAll());
    file.close();
    return content;
}

bool FileUtil::writeContent(const QString &filePath, const QString &content)
{
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly)) return false;
    file.write(content.toUtf8());
    file.close();
    return true;
}

bool FileUtil::isFileExist(const QString &filePath)
{
    QFileInfo file(filePath);
    return file.exists() && !file.isDir();
}

bool FileUtil::isDirectoryExist(const QString &dirPath)
{
    QDir dir(dirPath);
    return dir.exists() && !dirPath.isEmpty();
}

bool FileUtil::removeFile(const QString &filePath)
{
    return QFile::remove(filePath);
}

bool FileUtil::removeDirectory(const QString &dirPath)
{
    QDir dir(dirPath);
    if (dir.exists())
    {
        const QFileInfoList files = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files | QDir::Hidden);
        for (const QFileInfo &file : files)
        {
            if (file.isDir()) removeDirectory(file.absoluteFilePath());
            else QFile::remove(file.absoluteFilePath());
        }
        dir.removeRecursively();
    }
    return true;
}

bool FileUtil::hideFile(const QString &filePath)
{
    if (!isFileExist(filePath)) return false;
#ifdef Q_OS_WIN32
    QString commandLine = "attrib +s +h \"" + filePath + "\"";
    CommandUtil::runCommand(commandLine);
    return true;
#endif
#ifdef Q_OS_LINUX
    return false;
#endif
}

bool FileUtil::hideDirectory(const QString &dirPath)
{
    if (!isDirectoryExist(dirPath)) return false;
#ifdef Q_OS_WIN32
    QString dir = dirPath;
    if (dir[dir.length() - 1] == QDir::separator())
    {
        dir = dir.left(dir.size() - 1);
    }
    QString commandLine = "attrib +s +a +h +r \"" + dir + "\"";
    CommandUtil::runCommand(commandLine);
    return true;
#endif
#ifdef Q_OS_LINUX
    return false;
#endif
}

bool FileUtil::copyDirectory(const QString &dirPath, const QString &newPath)
{
    QDir sourceDir(dirPath);
    QDir targetDir(newPath);
    if (!targetDir.exists())
    {
        if (!targetDir.mkdir(targetDir.absolutePath()))
            return false;
    }

    const QFileInfoList fileInfoList = sourceDir.entryInfoList();
    for (const QFileInfo &fileInfo : fileInfoList)
    {
        if (fileInfo.fileName() == "." || fileInfo.fileName() == "..") continue;

        if (fileInfo.isDir())
        {
            if (!copyDirectory(fileInfo.filePath(), targetDir.filePath(fileInfo.fileName())))
                return false;
        }
        else
        {
            targetDir.remove(fileInfo.fileName());
            QFile::copy(fileInfo.filePath(), targetDir.filePath(fileInfo.fileName()));
        }
    }
    return true;
}

bool FileUtil::createDirectory(const QString &dirPath)
{
    QDir dir(dirPath);
    if (!dir.exists())
    {
        return dir.mkpath(dirPath);
    }
    return true;
}

bool FileUtil::createFileDirectory(const QString &filePath)
{
    return createDirectory(PathUtil::getDirectoryName(filePath));
}

qint64 FileUtil::fileSize(const QString &filePath)
{
    QFile file(filePath);
    if (!file.exists()) return 0;
    return file.size();
}

QString FileUtil::fileMd5(const QString &filePath)
{
    QFile sourceFile(filePath);
    if (!sourceFile.exists() || !sourceFile.open(QIODevice::ReadOnly))
    {
        return "";
    }
    qint64 fileSize = sourceFile.size();
    if (fileSize == 0)
    {
        return "";
    }
    const qint64 bufferSize = 10240;

    char buffer[bufferSize];
    int bytesRead;
    int readSize = qMin(fileSize, bufferSize);

    QCryptographicHash hash(QCryptographicHash::Md5);

    while (readSize > 0 && (bytesRead = sourceFile.read(buffer, readSize)) > 0)
    {
        fileSize -= bytesRead;
        hash.addData(buffer, bytesRead);
        readSize = qMin(fileSize, bufferSize);
    }

    sourceFile.close();
    return QString(hash.result().toHex());
}
